package com.fireXXX.job.analysis.crawler.common;

import com.fireXXX.job.analysis.crawler.error.TaskTypeEndCrawlerException;
import com.fireXXX.job.analysis.crawler.util.JacksonUtil;
import com.fireXXX.job.analysis.crawler.util.http.RestTemplateHandler;
import com.fireXXX.job.analysis.crawler.util.http.valid.ValidRestTemplateHandler;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Component
@Slf4j
public class DynamicProxy {

    private String lastCheckTime = "2022-01-23 22:31:01";
    private String baseUrl = "https://www.kuaidaili.com/free/intr/%d/";

    private String reqUrl = "https://www.baidu.com";

    @Autowired
    private RestTemplateHandler restTemplateHandler;

    @Autowired
    private ValidRestTemplateHandler validRestTemplateHandler;

    private Lock inUsingLock = new ReentrantLock();
    private Lock inBackupLock = new ReentrantLock();
    private Lock inWaitingLock = new ReentrantLock();

    private List<String> ipsInUsing = new ArrayList<String>() {{
    }};
//    private List<String> ipsInUsing = JacksonUtil.toObjectList("[\"124.65.5.166:9999\",\"117.141.155.242:53281\",\"124.65.5.166:9999\",\"211.136.128.154:53281\",\"14.215.224.254:3128\",\"124.65.5.166:9999\",\"124.64.8.50:8000\",\"120.196.112.6:3128\",\"211.136.128.154:53281\",\"14.215.224.254:3128\",\"223.100.215.24:8080\",\"117.141.155.244:53281\",\"124.70.46.14:3128\",\"223.100.215.26:8080\",\"61.153.251.150:22222\",\"124.65.5.166:9999\",\"106.54.141.54:3128\",\"202.102.86.228:8080\",\"125.46.0.62:53281\",\"125.46.0.62:53281\",\"223.100.215.24:8080\",\"61.153.251.150:22222\",\"124.70.46.14:3128\",\"211.136.128.154:53281\",\"124.64.8.50:8000\",\"60.211.218.78:53281\",\"221.226.75.86:55443\"]", String.class);
    private List<String> ipsBackup = new ArrayList<String>() {{
    }};
    private List<String> ipsWaiting = new ArrayList<String>() {{
    }};
    private Map<String, Long> ipsBlack = new ConcurrentHashMap<>();

    public DynamicProxy() throws IOException {
    }

    public String getIP() {
        if (ipsInUsing.isEmpty()) {
            switchIps();
        }

        inUsingLock.lock();
        if (ipsInUsing.isEmpty()) {
            inUsingLock.unlock();
            return null;
        }
        String ip = ipsInUsing.get(new Random().nextInt(ipsInUsing.size()));
        if (ip == null) {
            inUsingLock.unlock();
            return null;
        }

        inUsingLock.unlock();
        return ip;
    }

    @Scheduled(cron = "0 0/10 * * * ?")
    public void checkValid() {
        List<String> validUrls = new ArrayList<>();
        for (String ip : ipsBackup) {
            String[] part = ip.split(":");
            if (checkProxyIp(part[0], Integer.parseInt(part[1]), reqUrl) && !ipsBlack.containsKey(ip)) {
                validUrls.add(ip);
            }
        }

        inWaitingLock.lock();
        ipsBackup = validUrls;
        inWaitingLock.unlock();

        switchIps();
    }

    @Scheduled(cron = "0 0/30 * * * ?")
    public void grabNewProxy() {
        for (int i = 0; i < 10; i++) {
            try {
                String content = restTemplateHandler.doGet(String.format(baseUrl, i + 1), String.class);
                Document document = Jsoup.parseBodyFragment(content);
                //获取body
                Element body = document.body();
                Elements ipsElements = body.getElementById("list").getElementsByTag("tbody").get(0).children();
                for (Element element : ipsElements) {
                    String ip = element.child(0).text();
                    int port = Integer.parseInt(element.child(1).text());
                    float respTime = Float.parseFloat(element.child(5).text().replaceAll("秒", ""));
                    String validTime = element.child(6).text();

                    if (checkProxyIp(ip, port, reqUrl) && !ipsBlack.containsKey(ip + port)) {
                        inWaitingLock.lock();
                        ipsWaiting.add(ip + ":" + port);
                        inWaitingLock.unlock();

                        System.out.println("this.add(\"" + ip + ":" + port + "\");");
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void signBlackIP(String ip) {
        if (ip != null) {
            ipsBlack.put(ip, System.currentTimeMillis());

            inUsingLock.lock();
            ipsInUsing.remove(ip);
            inUsingLock.unlock();
        }
    }

    public void switchIps() {
        inBackupLock.lock();
        inWaitingLock.lock();
        ipsBackup = ipsWaiting;
        inWaitingLock.unlock();
        inBackupLock.unlock();

        inBackupLock.lock();
        inUsingLock.lock();
        ipsInUsing = ipsBackup;
        inUsingLock.unlock();
        inBackupLock.unlock();

        log.info("now proxy list (%s)", JacksonUtil.toJsonStringSafe(ipsInUsing));
    }

    private boolean checkProxyIp(String ip, int port, String reqUrl) {
        int statusCode = 0;
        try {
            validRestTemplateHandler.setHttpProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ip, port)));
            statusCode = validRestTemplateHandler.doGetStatusCode(reqUrl, String.class);
        } catch (Exception e) {
            System.out.println("ip " + ip + " is not available");
        }

        if (statusCode > 0) {
            return true;
        }

        return false;
    }

}
